﻿

Imports System.Threading.Tasks

Public MustInherit Class CSourceAbstract

	Public Event CopyStart(key As String, number As Integer, total As Integer, fileSize As Long)
	Public Event CopyFinish(key As String, copiedBytes As Long, totalBytes As Long, start As DateTime)
	Public Event CopyFailed(key As String, ex As Exception)
	Public Event Notify(msg As String)

	Public MustOverride ReadOnly Property Name As String
	'Public MustOverride Function GetKeys() As List(Of String)
	Public MustOverride Function GetKeysAndSizes() As CKeysAndSizes

	Protected MustOverride Sub CopyTo(destination As CSourceS3, key As String)
	Protected MustOverride Sub CopyTo(destination As CSourceFolder, key As String)


	Public Sub CopyTo(destination As CSourceAbstract, toCopy As CKeysAndSizes)
		'Trivial check, Logging
		If toCopy.Count = 0 Then
			RaiseEvent Notify("No changes to sync")
			Exit Sub
		End If
		LogPendingCopy(toCopy.Count, destination.Name, toCopy.Total)

		'Sort list, prepare a byte counter
		Dim copiedBytes As Long = 0
		Dim keys As New List(Of String)(toCopy.Keys)
		keys.Sort()

		'Iterate
		If TypeOf destination Is CSourceFolder Then
			Dim smallThenBig As New List(Of String)(keys)
			smallThenBig.Sort(New CSortBySizeAsc(toCopy))

			For Each key As String In smallThenBig 'keys
				CopyTo(destination, key, toCopy, keys.IndexOf(key), copiedBytes)
			Next
		Else
			'Organise as small ones first, then big ones
			'Dim smallOnes As New List(Of String)(keys.Count)
			'Dim bigOnes As New List(Of String)(keys.Count)
			'For Each key As String In keys
			'    If toCopy(key) < 4000000 Then smallOnes.Add(key) Else bigOnes.Add(key)
			'Next
			'Dim smallThenbig As New List(Of String)(keys.Count)
			'smallThenbig.AddRange(smallOnes)
			'smallThenbig.AddRange(bigOnes)

			Dim smallThenBig As New List(Of String)(keys)
			smallThenBig.Sort(New CSortBySizeAsc(toCopy))

			'Upload in that order
			Dim po As New ParallelOptions()
			po.MaxDegreeOfParallelism = 1
			Parallel.ForEach(Of String)(smallThenBig, po, Sub(key As String) CopyTo(destination, key, toCopy, smallThenBig.IndexOf(key), copiedBytes))

		End If
	End Sub
	Public Sub CopyTo(destination As CSourceAbstract, key As String, context As CKeysAndSizes, index As Integer, ByRef copiedBytes As Long)
		SyncLock (destination)
			RaiseEvent CopyStart(key, 1 + index, context.Count, context(key))
		End SyncLock

		Try
			If TypeOf destination Is CSourceS3 Then
				CopyTo(CType(destination, CSourceS3), key)
			ElseIf TypeOf destination Is CSourceFolder Then
				CopyTo(CType(destination, CSourceFolder), key)
			Else
				Throw New Exception
			End If

			copiedBytes += context(key)

			SyncLock (destination)
				RaiseEvent CopyFinish(key, copiedBytes, context.Total, context.Created)
			End SyncLock
		Catch ex As Exception
			SyncLock (destination)
				RaiseEvent CopyFailed(key, ex)
			End SyncLock
		End Try
	End Sub

	Protected Sub LogPendingCopy(count As Integer, dest As String, totalBytes As Long)
		If count = 0 Then
			Log("=>", vbTab, "No files to copy from ", Name, " to ", dest)
		Else
			Log("=>", vbTab, CUtilities.CountSummary(count, "file"), " to copy from ", Name, " to ", dest, " (", CUtilities.FileSize(totalBytes), ")")
		End If
	End Sub
	Protected Sub LogReadStart()
		Log("Getting info for ", Name, " ...")
	End Sub
	Protected Sub LogReadFinish(count As Integer, totalBytes As Long)
		Log(vbTab, CUtilities.CountSummary(count, "file"), vbTab, "(", CUtilities.FileSize(totalBytes), ")")
	End Sub

	Protected Sub Log(ParamArray msgs As String())
		Log(String.Concat(msgs))
	End Sub
	Protected Sub Log(msg As String)
		RaiseEvent Notify(msg)
	End Sub



	Private Class CSortBySizeAsc : Implements IComparer(Of String)
		Private m_dict As Dictionary(Of String, Long)
		Public Sub New(d As Dictionary(Of String, Long))
			m_dict = d
		End Sub
		Public Function Compare(x As String, y As String) As Integer Implements System.Collections.Generic.IComparer(Of String).Compare
			Return m_dict(x).CompareTo(m_dict(y))
		End Function
	End Class
End Class
